﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2012-02-18 20:02:23Z</CreationDate>
</File>
*/
#endregion


using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

#if WINDOWS_UWP || NETFX_CORE
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#else
using System.Windows;
using System.Windows.Controls;
#endif

using Outcoder.ComponentModel;

namespace Outcoder.UI.Xaml.Controls
{
	/* TODO: [DV] Comment. */
	public class ValidationSummary : Control
	{
		readonly ObservableCollection<object> errors 
						= new ObservableCollection<object>();

		readonly Dictionary<string, List<object>> errorLookup 
			                    = new Dictionary<string, List<object>>();
		const int lineHeight = 30;

		public IEnumerable<object> Errors
		{
			get
			{
				return errors;
			}
		}

		public ValidationSummary()
		{
			this.DefaultStyleKey = typeof(ValidationSummary);

			Loaded += HandleLoaded;
		}

		bool loaded;

		void HandleLoaded(object sender, RoutedEventArgs e)
		{
            if (loaded)
            {
                return;
            }
		    loaded = true;

#if SILVERLIGHT
			Page page = this.GetVisualAncestors<Page>().FirstOrDefault();

			if (page == null)
			{
				throw new InvalidOperationException(
					            "Unable to locate parent page.");
			}
			page.BindingValidationError += HandleBindingValidationError;

			DataContextChangedListener.Subscribe(this, HandleDataContextChanged);
#else
			throw new NotImplementedException("This control has only been implemented for Silverlight.");
#endif
		}

#if SILVERLIGHT
		/// <summary>
		/// Performs property setter validation.
		/// </summary>
		void HandleBindingValidationError(object sender, ValidationErrorEventArgs e)
        {
            string content = e.Error.ErrorContent.ToString();

            if (e.Action == ValidationErrorEventAction.Added)
            {
                AddError(content);
            }
            else
            {
                RemoveError(content);
            }
        }
#endif

		void HandleDataContextChanged(
			DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			/* Unsubscribe to previous notifier. */
			INotifyDataErrorInfo oldNotifier = e.OldValue as INotifyDataErrorInfo;
			if (oldNotifier != null)
			{
				oldNotifier.ErrorsChanged -= HandleErrorsChanged;
			}

			/* When the DataContext is an INotifyDataErrorInfo, 
			 * monitor it for errors. */
			INotifyDataErrorInfo notifier = DataContext as INotifyDataErrorInfo;
			if (notifier != null)
			{
				notifier.ErrorsChanged += HandleErrorsChanged;
			}
		}

        /// <summary>
        /// Performs Silverlight 4 input validation.
        /// </summary>
		void HandleErrorsChanged(object sender, DataErrorsChangedEventArgs args)
		{
			INotifyDataErrorInfo notifier = sender as INotifyDataErrorInfo;
			if (notifier == null)
			{
				return;
			}

			string propertyName = args.PropertyName;
			if (string.IsNullOrEmpty(propertyName))
			{
				return;
			}

			IEnumerable notifierErrors = notifier.GetErrors(args.PropertyName);

			List<object> errorList;
			if (!errorLookup.TryGetValue(propertyName, out errorList))
			{
				errorList = new List<object>();
			}

			foreach (var error in errorList)
			{
				RemoveError(error);
			}

			errorList.Clear();

			foreach (var error in notifierErrors)
			{
                AddError(error);
			}
			errorLookup[propertyName] = errorList;

			foreach (var error in errorList)
			{
				AddError(error);
			}
		}

        void AddError(object error)
        {
            string content = error.ToString();

            if (!errors.Contains(content))
            {
                errors.Add(content);
            }
            MinHeight += lineHeight;
        }

        void RemoveError(object error)
        {
            string content = error.ToString();

            errors.Remove(content);
            MinHeight -= lineHeight;
        }
	}
}
